# python模块
# Copyright belongs to 令狐冰璃 UTF-8 V3.0  2024.10.18
# 少女祈祷
import os
import re
import sched
import shutil
import subprocess
import sys
import time
import tkinter as tk
from tkinter import filedialog, messagebox
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from colorama import Fore, init
# 自定义模块
import backups
import ping
import iteration

# 初始化 colorama
init(autoreset=True)

# 检查更新
current_version = '1.5.6'
# 未发行版本
iteration.check_for_updates(current_version)

# 初始化目录路径
directory_path = None
backup_directory_path = None  # 初始化备份目录路径
stop_flag = False
start_button = None
timer_timer = None
save_timer = None
errip = 0
ip_pattern = re.compile(r'^静态IP=(\d+\.\d+\.\d+\.\d+)')
ips = []
seet = set()

# 配置文件
def read_config_file():
    # 获取可执行文件的路径
    if getattr(sys, 'frozen', False):
        current_directory = os.path.dirname(sys.executable)
    else:
        current_directory = os.path.dirname(os.path.abspath(__file__))
    # 构建配置文件的完整路径
    config_file_path = os.path.join(current_directory, 'config.blk')
    # 检查文件是否存在
    if os.path.exists(config_file_path):
        # 文件存在，读取内容
        with open(config_file_path, 'r', encoding='utf-8') as file:
            content = file.read()
        return content
    else:
        # 文件不存在，返回 None
        print(f"配置文件 {config_file_path} 不存在，跳过读取。")
        with open('log.txt', 'a', encoding='utf-8') as f:
            f.write(f"配置文件 {config_file_path} 不存在，跳过读取。\n")
        return None

def parse_config_content(content):
    pattern = re.compile(r'directory_path=(.*?)\nbackup_directory_path=(.*)')
    match = pattern.search(content)
    if match:
        directory_path = match.group(1).strip()
        backup_directory_path = match.group(2).strip()
        return directory_path, backup_directory_path
    else:
        return None, None


config_content = read_config_file()
if config_content is not None:
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"配置文件内容：{config_content}\n")

    # 解析配置文件内容
    directory_path, backup_directory_path = parse_config_content(config_content)
    if directory_path and backup_directory_path:
        print(f"directory_path: {directory_path}")
        print(f"backup_directory_path: {backup_directory_path}")
        with open('log.txt', 'a', encoding='utf-8') as f:
            f.write(f"directory_path: {directory_path}\n")
            f.write(f"backup_directory_path: {backup_directory_path}\n")
    else:
        print("未能解析配置文件内容。")
        with open('log.txt', 'a', encoding='utf-8') as f:
            f.write("未能解析配置文件内容。\n")
else:
    print("配置文件未找到。")

def select_directory():
    global directory_path
    directory_path = filedialog.askdirectory()
    if directory_path:
        update_label(path_label, fr"选择的目录: {directory_path}")
        load_ini_files()


def backup_select_directory():
    global backup_directory_path
    backup_directory_path = filedialog.askdirectory()
    if backup_directory_path:
        update_label(backup_path_label, fr"保存的目录: {backup_directory_path}")
        load_ini_files()


def backup_ini_files():
    print("开始备份 INI 文件")
    backups.backup_ini_files(directory_path, backup_directory_path)
    print(fr"备份完成,路径 -> {backup_directory_path}")


def update_label(label, text):
    label.config(text=text)


def load_ini_files():
    global ips, seet, directory_path
    seet.clear()
    ips.clear()
    # print(directory_path)
    ini_files = [os.path.join(directory_path, f) for f in os.listdir(directory_path) if f.endswith('.ini')]
    for file_name in ini_files:
        try:
            with open(file_name, 'r', encoding='gbk') as file:  # 尝试使用 gbk 编码
                lines = file.readlines()
                if len(lines) > 1:
                    match = ip_pattern.match(lines[1].strip())
                    if match:
                        ip = match.group(1)
                        ips.append(ip)
                        seet.add(ip)
        except UnicodeDecodeError:
            print(f"无法解码文件 {file_name}，跳过该文件")
    ips = list(seet)


def log_info(message):
    print(Fore.GREEN + message)


def log_warning(message):
    print(Fore.YELLOW + message)


def log_error(message):
    print(Fore.RED + message)

#线程锁
lock = threading.Lock()

def ping_ip(ip, num, total_ips):
    global stop_flag, errip, directory_path
    if stop_flag:
        return
    try:
        result = ping.pingip(ip)
        # print(f"Debug: ping result for {ip}: {result}")   # 调试台
        if "Destination host unreachable" in result or "Request timed out" in result or "Ping超时" in result:
            if all("Destination host unreachable" in line or "Request timed out" in line or "Ping超时" in line for line
                   in result.splitlines()):
                log_error(f"ERROR -> ping {ip} -> 超时    ")
                errip += 1
                with open('log.txt', 'a', encoding='utf-8') as f:
                    f.write(f"ERROR -> ping {ip} -> 超时    \n")
                # print(f"查找超时ip：{ip}")    # 输出调试信息
                temp_ip = ip
                found_file = None  # 用于存储找到的文件名
                found_files = []

                with lock:
                    for filename in os.listdir(directory_path):
                        if filename.endswith('.ini'):
                            file_path = os.path.join(directory_path, filename)  # 获取文件的绝对路径
                            with open(file_path, 'r', encoding='gbk') as f:
                                content = f.read()
                                if temp_ip in content:
                                    found_files.append(file_path)

                if found_files:
                    error_ip_dir = os.path.join(os.path.dirname(directory_path), 'ErrorIP')
                    os.makedirs(error_ip_dir, exist_ok=True)

                    # 创建 error_ips.txt 文件
                    error_ips_file = os.path.join(error_ip_dir, 'error_ips.txt')

                    # 写入超时IP
                    with open(error_ips_file, 'a', encoding='utf-8') as f:
                        f.write(f"-----分--割--线-----\n")
                        f.write(f"超时IP: {temp_ip}\n")

                    # 记录找到的文件信息
                    with open(error_ips_file, 'a', encoding='utf-8') as f:
                        f.write("找到的文件:\n")
                        for file in found_files:
                            file_name = os.path.basename(file)
                            f.write(f"{file_name} 中找到 超时IP -> “{temp_ip}”  |\n")
                            print(Fore.RED + f"{file_name} 中找到 超时IP -> “{temp_ip}”  |")
                            with open('log.txt', 'a', encoding='utf-8') as log:
                                log.write(f"{file_name} 中找到 超时IP -> “{temp_ip}”  |\n")

            # ===分==割==线===
            else:
                log_warning(f"WARN -> ping {ip} -> 有一个没有通过  ")
                errip += 1
                with open('log.txt', 'a', encoding='utf-8') as f:
                    f.write(f"WARN -> ping {ip} -> 有一个没有通过    \n")
        else:
            log_info(f"INFO -> ping {ip} -> 全部通过    ")
            with open('log.txt', 'a', encoding='utf-8') as f:
                f.write(f"INFO -> ping {ip} -> 全部通过    \n")

        with open('ping.txt', 'a', encoding='utf-8') as f:
            f.write(f"正在 Ping {ip} 具有 32 字节的数据:\n")
            f.write(result)
            f.write(f"\n{ip} 的 Ping 统计信息:\n")
            f.write(f"INFO -> ping {ip} n -> {total_ips - num - 1}\n")
            f.write(f"==================\n")
    except subprocess.TimeoutExpired:
        log_error(f"ERROR -> ping {ip} 超时")
        errip += 1
        with open('ping.txt', 'a', encoding='utf-8') as f:
            f.write(f"ERROR -> ping {ip} 超时\n")
            f.write(f"==================\n")
    except Exception as e:
        log_error(f"ERROR -> ping {ip} 发生错误: {e}")
        with open('ping.txt', 'a', encoding='utf-8') as f:
            f.write(f"ERROR -> ping {ip} 发生错误: {e}\n")
            f.write(f"==================\n")
    return errip

def main():
    global stop_flag,errip,directory_path
    load_ini_files()
    stop_flag = False
    total_ips = len(ips)
    with ThreadPoolExecutor(max_workers=4) as executor:  # 设置线程池的最大线程数
        futures = []
        for num, ip in enumerate(ips):
            futures.append(executor.submit(ping_ip, ip, num, total_ips))

        # 等待所有任务完成
        for future in as_completed(futures):
            future.result()
    print(f"已完成 -> 找到IP总个数 {len(ips)}，不通过个数 {errip}")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"已完成 -> 找到IP总个数 {len(ips)}，不通过个数 {errip}\n")
        f.write(f'结束 时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n')
    start_buttons.config(state=tk.NORMAL)
    start_button_save.config(state=tk.NORMAL)
    start_button_ping.config(state=tk.NORMAL)
    button_save.config(state=tk.NORMAL)
    backup_browse_button.config(state=tk.NORMAL)
    browse_button.config(state=tk.NORMAL)
    configfile.config(state=tk.NORMAL)


def start_ping():
    global errip
    # 删除ErrorIP文件夹
    error_ip_dir = os.path.join(os.path.dirname(directory_path), 'ErrorIP')
    shutil.rmtree(error_ip_dir, ignore_errors=True)
    # 重置初始化
    errip =0

    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f'尝试 删除ErrorIP 文件夹\n')
        f.write(f"尝试 Ping 操作\n")
    # 创建一个新的线程来运行 main 函数，避免阻塞 GUI
    global stop_flag
    stop_flag = False
    if start_buttons is not None:
        # 锁定
        start_buttons.config(state=tk.DISABLED)
        start_button_save.config(state=tk.DISABLED)
        start_button_ping.config(state=tk.DISABLED)
        button_save.config(state=tk.DISABLED)
        backup_browse_button.config(state=tk.DISABLED)
        browse_button.config(state=tk.DISABLED)
        configfile.config(state=tk.DISABLED)
    else:
        log_info("开始按钮未初始化")
    threading.Thread(target=main).start()

def stop_ping():
    global stop_flag
    stop_flag = True
    if start_buttons is not None:
        # 解锁
        start_buttons.config(state=tk.NORMAL)
        start_button_save.config(state=tk.NORMAL)
        start_button_ping.config(state=tk.NORMAL)
        button_save.config(state=tk.NORMAL)
        backup_browse_button.config(state=tk.NORMAL)
        browse_button.config(state=tk.NORMAL)
        configfile.config(state=tk.NORMAL)
    else:
        log_info("开始按钮未初始化")
    log_info("停止 Ping 操作")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"停止 Ping 操作\n")

# 创建调度器
scheduler = sched.scheduler(time.time, time.sleep)
running = False

# 定义定时任务
def timed_event():
    print("定时任务执行中...")
    print(f"当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    start_ping()
    if running:
        scheduler.enter(timer_interval * 60, 1, timed_event)



# 定义启动定时任务的函数
def start_time(interval):
    global running
    running = True
    scheduler.enter(interval * 60, 1, timed_event)  # 将分钟转换为秒
    # 启动一个新的线程来运行调度器
    threading.Thread(target=run_sched).start()


# 定义停止定时任务的函数
def stop_time():
    print(f"挂起已离线,当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"挂起自动ping已离线,当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")
    # 解锁输入
    timer_entry.config(state=tk.NORMAL)
    global running
    running = False
    # 清空调度器中的所有任务
    for event in scheduler.queue:
        scheduler.cancel(event)


# 定义运行调度器的函数
def run_sched():
    while running:
        scheduler.run(blocking=False)
        time.sleep(1)


# 定义按钮点击事件处理函数
def on_Timebutt():
    print(f"确认挂起程序,当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    global timer_interval,running
    if running:
        stop_time()
    else:
        try:
            timer_interval = int(timer_entry.get())
            if timer_interval < 30:
                messagebox.showerror("错误", "请输入大于30的分钟数")
                print("挂起失败")
            elif timer_interval > 1440:
                messagebox.showerror("错误", "最大挂起时间为24小时")
                print("挂起失败")
            else:
                start_time(timer_interval)
                # 锁输入
                timer_entry.config(state=tk.DISABLED)
                with open('log.txt', 'a', encoding='utf-8') as f:
                    f.write(f"挂起自动ping成功,当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")
        except ValueError:
            messagebox.showerror("错误", "请输入有效的分钟数")
            print("挂起失败")

# 自动保存函数

scheduler2 = sched.scheduler(time.time, time.sleep)
running2 = False

def timed_event_save():
    print("自动保存已触发")
    print(f"保存时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"自动保存时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")
    backups.backup_ini_files(directory_path, backup_directory_path)
    # 重启调度器
    if running2:
        interval_seconds = save_interval_hours * 3600
        scheduler2.enter(interval_seconds, 1, timed_event_save)

def start_time_save(interval):
    global running2
    running2 = True
    interval_seconds = interval * 3600  # 将小时转换为秒
    print(f"启动自动保存任务，间隔: {interval_seconds} 秒")
    scheduler2.enter(interval_seconds, 1, timed_event_save)
    # 启动一个新的线程来运行调度器
    threading.Thread(target=run_sched_save).start()

def stop_time_save():
    print(f"自动保存已关闭")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"挂起自动保存已离线 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")
    # 解锁输入
    save_entry.config(state=tk.NORMAL)
    global running2
    running2 = False
    # 清空调度器中的所有任务
    for event in scheduler2.queue:
        scheduler2.cancel(event)

def run_sched_save():
    while running2:
        # print("调度器运行中...")
        scheduler2.run(blocking=False)
        time.sleep(1)

def save_interval():
    global save_interval_hours, save_entry
    print(f"保存间隔,当前时间 -> : {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    try:
        save_interval_hours = float(save_entry.get())
        print(f"新的保存间隔: {save_interval_hours} 小时")
        if save_interval_hours < 1:
            messagebox.showerror("错误", "请输入大于1小时的小时数")
            print("挂起失败")
        elif save_interval_hours > 48:
            messagebox.showerror("错误", "请输入小于49小时的小时数")
            print("挂起失败")
        else:
            start_time_save(save_interval_hours)
            # 锁输入
            save_entry.config(state=tk.DISABLED)
            with open('log.txt', 'a', encoding='utf-8') as f:
                f.write(f"挂起自动保存成功 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")
    except ValueError:
        messagebox.showerror("错误", "请输入有效的小时数")
        print("挂起失败")

def config_file():
    print(f"配置文件已保存,当前时间 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
    with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(f"配置文件已保存，打开绝对路径{directory_path}，保存绝对路径{backup_directory_path}\n")
    with open('config.blk', 'w', encoding='utf-8') as f:
        f.write(f"directory_path={directory_path}\n")
        f.write(f"backup_directory_path={backup_directory_path}\n")


# 创建 GUI
def create_gui():
    root = tk.Tk()
    root.title(f"云机IP检测工具v{current_version}")
    root.geometry("680x420")

    def off_window():
        result = messagebox.askquestion("警告", "退出程序后后台挂起离线，是否退出")
        if result == 'yes':
            button = messagebox.askyesno("确认", "确定要退出吗？")
            if button:
                root.destroy()
                with open('log.txt', 'a', encoding='utf-8') as f:
                    f.write(f"进程已结束 退出代码为 0 -> {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}\n")

    root.protocol("WM_DELETE_WINDOW", off_window)

    # 标签
    global path_label, backup_path_label, timer_entry,save_entry
    path_label = tk.Label(root, text=r"选择的目录: " + directory_path)
    path_label.place(x=10, y=50)

    label = tk.Label(root, text="点击按钮开始 Ping 操作")
    label.place(x=10, y=10)

    backup_path_label = tk.Label(root, text=r"保存的目录: " + backup_directory_path)
    backup_path_label.place(x=10, y=120)

    label = tk.Label(root, text="分钟 自动ping")
    label.place(x=90, y=195)

    label = tk.Label(root, text="小时 自动保存")
    label.place(x=90, y=235)

    # 按钮
    global start_button, stop_button,start_buttons,start_button_save,start_button_ping,button_save,backup_browse_button,browse_button,configfile

    start_buttons = tk.Button(root, text="开始 Ping", command=start_ping)
    start_buttons.place(x=230, y=5)

    stop_button = tk.Button(root, text="停止", command=stop_ping)
    stop_button.place(x=300, y=5)

    browse_button = tk.Button(root, text="浏览", command=select_directory)
    browse_button.place(x=10, y=75)

    backup_browse_button = tk.Button(root, text="浏览", command=backup_select_directory)
    backup_browse_button.place(x=10, y=145)

    button_save = tk.Button(root, text="保存", command=backup_ini_files)
    button_save.place(x=50, y=145)

    configfile = tk.Button(root, text="保存路径", command=config_file)
    configfile.place(x=120, y=145)

    start_button_ping = tk.Button(root, text="挂起", command=on_Timebutt)
    start_button_ping.place(x=300, y=190)

    stop_button_ping = tk.Button(root, text="关闭", command=stop_time)
    stop_button_ping.place(x=350, y=190)

    start_button_save = tk.Button(root, text="挂起", command=save_interval)
    start_button_save.place(x=300, y=230)

    stop_button_save = tk.Button(root, text="关闭", command=stop_time_save)
    stop_button_save.place(x=350, y=230)

    # 输入框
    timer_interval = 30
    timer_entry = tk.Entry(root, width=10)
    timer_entry.insert(0,str(timer_interval))
    timer_entry.place(x=10, y=195)

    save_interval_hours = 24
    save_entry = tk.Entry(root, width=10)
    save_entry.insert(0, str(save_interval_hours))
    save_entry.place(x=10, y=235)

    # 运行 GUI 主循环
    root.mainloop()


if __name__ == "__main__":
    try:
        create_gui()
    except KeyboardInterrupt:
        print("\n程序被用户中断")
        stop_ping()
